import type { ConversObject, Menu, QueryStringOptions } from "@/typings";
type ThrottledFunction<T extends (...args: any[]) => any> = (...args: Parameters<T>) => ReturnType<T>;

const isArray = Array.isArray;
const keys = Object.keys;
const typeMap = new Map<string, string>([
  ["string", "string"],
  ["number", "number"],
  ["boolean", "boolena"],
  ["bigint", "bigint"],
  ["symbol", "symbol"]
]);

/**
 * @description 获取浏览器默认语言
 * @returns {String}
 */
export function getBrowserLanguage(): string {
  const browserLang = window.navigator.language;
  let defaultBrowserLang = "";
  if (["cn", "zh", "zh-cn"].includes(browserLang.toLowerCase())) {
    defaultBrowserLang = "zh";
  } else {
    defaultBrowserLang = "en";
  }
  return defaultBrowserLang;
}

/**
 * @description 使用递归找出所有面包屑存储到 pinia/vuex 中
 * @param {Menu[]} menuList 菜单列表
 * @param {Array} parent 父级菜单
 * @param {Object} result 处理后的结果
 * @returns {Object}
 */
export const getAllBreadcrumbList = (menuList: Menu[], parent = [], result: { [key: string]: any } = {}) => {
  for (const item of menuList) {
    result[item.path] = [...parent, item];
    if (item.children) getAllBreadcrumbList(item.children, result[item.path], result);
  }
  return result;
};

/**
 * @description 使用递归扁平化菜单，方便加动态路由
 * @param {Array} menuList 菜单列表添
 * @returns {Array}
 */
export function getFlatMenuList(menuList: Menu[]): Menu[] {
  const newMenuList: Menu[] = deepClone(menuList);
  return newMenuList.flatMap((item) => [item, ...(item.children ? getFlatMenuList(item.children) : [])]);
}

/**
 * @description 节流
 * @param callback 回调
 * @param limit 时间
 * @returns {ThrottledFunction<T>}
 */
export function throttle<T extends (...args: any[]) => any>(callback: T, limit: number): ThrottledFunction<T> {
  let inThrottle: boolean = false;
  return (...args: Parameters<T>) => {
    if (!inThrottle) {
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
      return callback(...args);
    }
  };
}

/**
 * @description 防抖
 * @param func 回调
 * @param delay 时间
 * @returns {T}
 */
export function debounce<T extends (...args: any[]) => any>(func: T, delay: number = 0, context?: any): T {
  let timeoutId: number | undefined;
  const debouncedFunc = function (...args: any[]) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      func.apply(context, args);
    }, delay);
  };
  return debouncedFunc as T;
}

/**
 * @description 获取当前时间对应的提示语
 * @returns {String}
 */
export function getTimeState() {
  const timeNow = new Date();
  const hours = timeNow.getHours();
  if (hours >= 6 && hours <= 10) return `早上好呀！ ⛅`;
  if (hours >= 10 && hours <= 14) return `中午好呀！ 🌞`;
  if (hours >= 14 && hours <= 18) return `下午好呀！ 🌞`;
  if (hours >= 18 && hours <= 24) return `晚上好呀！ 🌛`;
  if (hours >= 0 && hours <= 6) return `凌晨好呀！ 🌛`;
}

/**
 * @description 深克隆
 * @param obj 克隆对象
 * @returns 返回新对象
 */
export function deepClone<T>(obj: T): T {
  if (obj === null || typeof obj !== "object") {
    return obj;
  }
  if (obj instanceof Date) {
    return new Date(obj.getTime()) as any;
  }
  if (obj instanceof Array) {
    const newArr = [];
    for (let i = 0; i < obj.length; i++) {
      newArr[i] = deepClone(obj[i]);
    }
    return newArr as any;
  }
  if (obj instanceof Object) {
    const newObj: any = {};
    for (const key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        newObj[key] = deepClone(obj[key]);
      }
    }
    return newObj as any;
  }
  return obj;
}

export const isUndefined = (value: any): boolean => {
  return typeof value === "undefined";
};

/**
 * 是否为空
 *
 * @static
 * @param {*} value
 * @returns {boolean}
 * @memberof Util
 */
export const isEmpty = (value: any): boolean => {
  return isUndefined(value) || Object.is(value, "") || value === null || value !== value;
};

/**
 * @description 是否为undefined或者null
 * @param value
 * @returns
 */
export function isUndefinedOrNull(value: any): boolean {
  return value === undefined || value === null;
}

/**
 * @description 比较两个值是否一致
 * @param value1 值1
 * @param value2 值2
 * @returns boolean
 */
export function isEqual(value1: any, value2: any): boolean {
  if (isUndefinedOrNull(value1) && isUndefinedOrNull(value2)) return true;
  if (typeMap.get(typeof value1)) return value1 === value2;
  if (typeof value1 !== "object" || typeof value2 !== "object") return false;
  if (isArray(value1) && isArray(value2) && value1.length === 0 && value2.length === 0) return true;
  if (!isArray(value1) && !isArray(value2) && keys(value1).length === 0 && keys(value2).length === 0) return true;
  if (isArray(value1) && isArray(value2)) {
    if (value1.length !== value2.length) return false;
    for (let i = 0; i < value1.length; i++) {
      if (!isEqual(value1[i], value2[i])) return false;
    }
    return true;
  }
  if (keys(value1).length !== keys(value2).length) return false;
  for (const key of keys(value1)) {
    if (!isEqual(value1[key], value2[key])) return false;
  }
  return true;
}

/**
 * @description 获取随机数
 * @param {number} start 开始位置
 * @param {number} end 结束位置
 * @param {number} decimal 小数位数 默认0
 * @returns {number}
 */
export const random = (start: number, end: number, decimal: number = 0): number => {
  if (start >= end) return end;
  const range = end - start;
  const rendomDecimal = Math.random() * range;
  const result = Number(rendomDecimal.toFixed(decimal));
  return Math.min(start, end) + result;
};

/**
 * @description 文件转base64
 * @param file 转换的文件
 * @returns
 */
export const fileToBase64 = async (file: File): Promise<string> => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = function () {
      resolve(reader.result as string);
    };
    reader.onerror = function () {
      reject(new Error("文件转base64失败"));
    };
  });
};

/**
 * @description 取改变值
 * @param newValue 新值
 * @param oldValue 旧值
 * @returns 返回改变之后的值
 */
export const unique = (newValue: any, oldValue: any) => {
  const result: any = {};
  for (const key in newValue) {
    if (newValue[key] !== oldValue[key]) {
      result[key] = oldValue[key];
    }
  }
  return result;
};

/**
 * @description 方案状态
 */
export const schemeStatusList = [
  {
    id: 1,
    label: "启用",
    value: true
  },
  {
    id: 2,
    label: "禁用",
    value: false
  }
];

/**
 * @description 请求参数处理
 * @param obj 处理目标对象
 * @param options 配置项
 * @param options.separator 连接符
 * @param options.prefix 前缀
 * @param options.suffix 后缀
 * @returns
 */
export const objToQueryString = <T extends ConversObject>(
  obj: T,
  options: QueryStringOptions = {},
  result: string = ""
): string => {
  const { separator = "&", prefix = "", suffix = "" } = options;
  if (typeof obj !== "object") return JSON.stringify(obj);
  result += prefix;
  result += Object.keys(obj)
    .map((key) => {
      if (!isEmpty(obj[key])) {
        return encodeURIComponent(key) + "=" + JSON.stringify(obj[key]);
      }
    })
    .filter((item) => item)
    .join(encodeURIComponent(separator));
  result += suffix;
  return result;
};
